// Description: 这是订单状态机的实现.
package hfsm

import (
	"context"
	"github.com/gogf/gf/v2/os/gctx"
	"hotgo/internal/consts"
	"hotgo/internal/dao"
	"hotgo/internal/model/entity"
	"hotgo/internal/model/input/adminin"
	"hotgo/internal/model/input/expressinfoin"
	"hotgo/internal/model/input/payin"
	"hotgo/internal/model/input/sysin"
	"hotgo/internal/service"
	"hotgo/utility/simple"
	"sync"
	"time"

	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/looplab/fsm"
)

// OrderFSM 订单状态机
// 使用文档 https://github.com/looplab/fsm
var OFSM *OrderFSM

type OrderFSM struct {
	ctx    context.Context
	mapper sync.Map
	//*fsm.FSM
}

func OrderFsmSetup(ctx context.Context) {
	OFSM = new(OrderFSM)
	OFSM.ctx = ctx
	// 初始化未到结束状态的订单
	var (
		data  []*sysin.UserOrderListModel
		total int
	)

	if err := g.DB().Model(dao.Order.Table()).WhereNotIn(dao.Order.Columns().Status, []string{consts.UserOrderStatusClose, consts.UserOrderStatusDone}).ScanAndCount(&data, &total, false); err != nil {
		g.Log().Warningf(ctx, "获取未完成的状态失败: %s", err.Error())
		return
	}
	for _, d := range data {
		OFSM.NewOrderFSM(d.Status, d.Id)
	}
}

// 最终状态 已完成 已关闭
// https://plantuml.shroot.dev/png/hP7FIiD04CRlynHpR5yXVPfgDnfO4cdMUt88Y7zebOWzA8W71Qa9MBM1OfwcEzcyXSsEDQ7YpSiEtyvlv_k6LPwI0atse2-kgzCFYc9wZytd748r72jmwFQPqdozcWt0Sx57irnF7_ccZ-fG-83ewENh5a9ii9yMeZqSNIPwScdUdWPMxp3DW6xRwkoSJgQiXJ_YAaF1G9KkJBwieuZc9JC7WGfmBDpZBUkFF1VupM-_Cg7xeawZF-Dqs_yTPrDWvuaSxWdPyaqWdSGcAnZeimsBTV7IvcezxQ8zEhkgLdVVF8iEOaC05xRSby4caDqmMTGtTZ832C0V6AhWw0i0
func (this *OrderFSM) NewOrderFSM(initialState string, orderID int64) {

	f := fsm.NewFSM(
		initialState,
		fsm.Events{
			// 从未支付
			{
				Name: consts.NotToPayEvent,
				Src:  []string{consts.UserOrderStatusNot},
				Dst:  consts.UserOrderStatusPay,
			}, // 已支付
			// 从未支付
			{
				Name: consts.NotToCloseEvent,
				Src:  []string{consts.UserOrderStatusNot},
				Dst:  consts.UserOrderStatusClose,
			}, // 已关闭
			// 从已支付
			{
				Name: consts.PayToWaitExpressEvent,
				Src:  []string{consts.UserOrderStatusPay},
				Dst:  consts.UserOrderStatusWaitExpress,
			}, // 待发货
			{
				Name: consts.PayToWaitVerifyEvent,
				Src:  []string{consts.UserOrderStatusPay},
				Dst:  consts.UserOrderStatusWaitVerify,
			}, // 待核销
			{
				Name: consts.PayToReturnRequestEvent,
				Src:  []string{consts.UserOrderStatusPay},
				Dst:  consts.UserOrderStatusReturnRequest,
			}, // 申请退款
			{
				Name: consts.PayToDoneEvent,
				Src:  []string{consts.UserOrderStatusPay},
				Dst:  consts.UserOrderStatusDone,
			}, // 已完成
			// 从待发货
			{
				Name: consts.WaitExpressToExpressedEvent,
				Src:  []string{consts.UserOrderStatusWaitExpress},
				Dst:  consts.UserOrderStatusExpressed,
			}, // 已发货
			{
				Name: consts.WaitExpressToReturnRequestEvent,
				Src:  []string{consts.UserOrderStatusWaitExpress},
				Dst:  consts.UserOrderStatusReturnRequest,
			}, // 申请退款
			// 从待核销
			{
				Name: consts.WaitVerifyToVerifiedEvent,
				Src:  []string{consts.UserOrderStatusWaitVerify},
				Dst:  consts.UserOrderStatusVerified,
			}, // 已核销
			{
				Name: consts.WaitVerifyToReturnRequestEvent,
				Src:  []string{consts.UserOrderStatusWaitVerify},
				Dst:  consts.UserOrderStatusReturnRequest,
			}, // 申请退款
			{
				Name: consts.WaitVerifyToCloseEvent,
				Src:  []string{consts.UserOrderStatusWaitVerify},
				Dst:  consts.UserOrderStatusClose,
			}, // 已关闭
			// 从已发货
			{
				Name: consts.ExpressedToDoneEvent,
				Src:  []string{consts.UserOrderStatusExpressed},
				Dst:  consts.UserOrderStatusDone,
			}, // 已完成
			{
				Name: consts.ExpressedToReturnRequestEvent,
				Src:  []string{consts.UserOrderStatusExpressed},
				Dst:  consts.UserOrderStatusReturnRequest,
			}, // 申请退款
			// 从待核销
			{
				Name: consts.VerifyToDoneEvent,
				Src:  []string{consts.UserOrderStatusWaitVerify},
				Dst:  consts.UserOrderStatusDone,
			}, // 已完成
			// 从申请退款
			{
				Name: consts.ReturnRequestToReturningEvent,
				Src:  []string{consts.UserOrderStatusReturnRequest},
				Dst:  consts.UserOrderStatusReturning,
			}, // 退款中
			{
				Name: consts.ReturnRequestToReturnRejectEvent,
				Src:  []string{consts.UserOrderStatusReturnRequest},
				Dst:  consts.UserOrderStatusReturnReject,
			}, // 拒绝退款
			// 从退款中
			{
				Name: consts.ReturningToReturnedEvent,
				Src:  []string{consts.UserOrderStatusReturning},
				Dst:  consts.UserOrderStatusReturned,
			}, // 已退款
			// 从已退款
			{
				Name: consts.ReturnedToCloseEvent,
				Src:  []string{consts.UserOrderStatusReturned},
				Dst:  consts.UserOrderStatusClose,
			}, // 已关闭
		},
		fsm.Callbacks{
			"enter_" + consts.UserOrderStatusPay:           func(_ context.Context, event *fsm.Event) { this.enterOrderPayHandle(orderID, event) },           // 已支付
			"enter_" + consts.UserOrderStatusWaitExpress:   func(_ context.Context, event *fsm.Event) { this.enterOrderWaitExpressHandle(orderID, event) },   // 待发货
			"enter_" + consts.UserOrderStatusWaitVerify:    func(_ context.Context, event *fsm.Event) { this.enterOrderWaitVerifyHandle(orderID, event) },    // 待核销
			"enter_" + consts.UserOrderStatusExpressed:     func(_ context.Context, event *fsm.Event) { this.enterOrderExpressedHandle(orderID, event) },     // 已发货
			"enter_" + consts.UserOrderStatusVerified:      func(_ context.Context, event *fsm.Event) { this.enterOrderVerifyHandle(orderID, event) },        // 已核销
			"enter_" + consts.UserOrderStatusDone:          func(_ context.Context, event *fsm.Event) { this.enterOrderDoneHandle(orderID, event) },          // 已完成
			"enter_" + consts.UserOrderStatusReturnRequest: func(_ context.Context, event *fsm.Event) { this.enterOrderReturnRequestHandle(orderID, event) }, // 申请退款
			"enter_" + consts.UserOrderStatusReturnReject:  func(_ context.Context, event *fsm.Event) { this.enterOrderReturnRejectHandle(orderID, event) },  // 拒绝退款
			"enter_" + consts.UserOrderStatusReturning:     func(_ context.Context, event *fsm.Event) { this.enterOrderReturningHandle(orderID, event) },     // 退款中
			"enter_" + consts.UserOrderStatusReturned:      func(_ context.Context, event *fsm.Event) { this.enterOrderReturnedHandle(orderID, event) },      // 已退款
			"enter_" + consts.UserOrderStatusClose:         func(_ context.Context, event *fsm.Event) { this.enterOrderCloseHandle(orderID, event) },         // 已关闭

			"enter_state": func(_ context.Context, event *fsm.Event) { this.enterOrderFSM(orderID, event) }, // 进入状态

			"after_" + consts.NotToPayEvent:                 func(_ context.Context, event *fsm.Event) { this.afterOrderPayHandle(orderID, event) },       // 已支付
			"after_" + consts.WaitVerifyToVerifiedEvent:     func(_ context.Context, event *fsm.Event) { this.afterOrderVerifyHandle(orderID, event) },    // 已核销
			"after_" + consts.VerifyToDoneEvent:             func(_ context.Context, event *fsm.Event) { this.afterOrderDoneHandle(orderID, event) },      // 已完成
			"after_" + consts.WaitExpressToExpressedEvent:   func(_ context.Context, event *fsm.Event) { this.afterOrderExpressedHandle(orderID, event) }, // 已发货
			"after_" + consts.ReturningToReturnedEvent:      func(_ context.Context, event *fsm.Event) { this.afterOrderReturnedHandle(orderID, event) },  // 已退款
			"after_" + consts.ReturnRequestToReturningEvent: func(_ context.Context, event *fsm.Event) { this.afterOrderReturningHandle(orderID, event) }, // 退款中
		})
	this.mapper.LoadOrStore(orderID, f)

	return
}

//func (this *OrderFSM) GetCurrentState() string {
//	return this.FSM.Current()
//}

func (this *OrderFSM) PushState(orderID int64, event string, args ...interface{}) (err error) {
	f := this.getFsm(orderID)
	//f.SetState(e)
	if err = f.Event(context.Background(), event, args...); err != nil {
		glog.Errorf(context.Background(), "变更状态失败: %s", err.Error())
		return gerror.Wrap(err, "变更状态失败")
	}
	return
}

func (this *OrderFSM) getFsm(orderID int64) *fsm.FSM {
	f, _ := this.mapper.Load(orderID)
	return f.(*fsm.FSM)
}

func (this *OrderFSM) enterOrderFSM(orderID int64, e *fsm.Event) {
	_, err := g.DB().Model(dao.Order.Table()).WherePri(orderID).Update(g.Map{
		"status": e.Dst,
	})
	if err != nil {
		g.Log().Errorf(context.Background(), "更新订单状态失败: %s", err.Error())
		return
	}
}

// 已支付
func (this *OrderFSM) enterOrderPayHandle(orderID int64, e *fsm.Event) {
	_, err := g.DB().Model(dao.Order.Table()).Data("pay_time", time.Now().Format(time.DateTime)).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新支付时间失败")
		return
	}

	// 这两个时间产品说要分开
	_, err = g.DB().Model(dao.Order.Table()).Data("confirm_time", time.Now().Format(time.DateTime)).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新订单确认时间失败")
		return
	}
}

// 待发货
func (this *OrderFSM) enterOrderWaitExpressHandle(orderID int64, e *fsm.Event) {

}

// 待核销
func (this *OrderFSM) enterOrderWaitVerifyHandle(orderID int64, e *fsm.Event) {

}

// 已核销
func (this *OrderFSM) enterOrderVerifyHandle(orderID int64, e *fsm.Event) {
	this.PushState(orderID, consts.VerifyToDoneEvent)
	_, err := g.DB().Model(dao.Order.Table()).Data("end_time", time.Now().Format(time.DateTime)).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新核销时间失败")
		return
	}
	var orderInfo entity.Order
	if err = dao.Order.Ctx(this.ctx).WherePri(orderID).Scan(&orderInfo); err != nil {
		glog.Errorf(context.Background(), "获取订单信息失败: %s", err.Error())
		return
	}
	go service.Distribution().Product(this.ctx, orderInfo.Uid)
}

// 已发货
func (this *OrderFSM) enterOrderExpressedHandle(orderID int64, e *fsm.Event) {
	//_, err := g.DB().Model(dao.Order.Table()).WherePri(orderID).Data("end_time", time.Now().Format(time.DateTime)).Update()
	//if err != nil {
	//	glog.Errorf(context.Background(), "更新发货时间失败")
	//	return
	//}
}

// 已完成
func (this *OrderFSM) enterOrderDoneHandle(orderID int64, e *fsm.Event) {
	_, err := g.DB().Model(dao.Order.Table()).Data("archive_time", time.Now().Format(time.DateTime)).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新核销时间失败")
		return
	}
}

// 申请退款
func (this *OrderFSM) enterOrderReturnRequestHandle(orderID int64, e *fsm.Event) {

}

// 拒绝退款
func (this *OrderFSM) enterOrderReturnRejectHandle(orderID int64, e *fsm.Event) {

}

// 退款中
func (this *OrderFSM) enterOrderReturningHandle(orderID int64, e *fsm.Event) {
	ctx := e.Args[0].(context.Context)
	order, err := service.SysUserOrder().View(ctx, &sysin.UserOrderViewInp{Id: orderID})
	if err != nil {
		glog.Errorf(context.Background(), "获取订单失败: %s", err.Error())
		return
	}
	//var payInfo entity.PayLog
	//if err = service.Pay().Model(ctx).Where(dao.PayLog.Columns().OrderSn, order.Uid).Scan(&payInfo); err != nil {
	//	glog.Errorf(context.Background(), "获取支付信息失败: %s", err.Error())
	//	return
	//}
	rs, err := service.PayRefund().Refund(ctx, &payin.PayRefundInp{
		OrderSn: order.Uid,
		//RefundMoney: payInfo.PayAmount, // 全额退款
		// 如果是组合订单，只退款非会员订单金额
		RefundMoney: order.UseAmount,
		Reason:      order.RefundReason,
		Remark:      "申请退款",
	})
	if err != nil {
		glog.Errorf(context.Background(), "退款失败: %s", err.Error())
		return
	}
	glog.Info(context.Background(), rs)

}

// 已退款
func (this *OrderFSM) enterOrderReturnedHandle(orderID int64, e *fsm.Event) {
	_, err := g.DB().Model(dao.Order.Table()).Data("end_time", time.Now().Format(time.DateTime)).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新核销时间失败")
		return
	}
}

// 已关闭
func (this *OrderFSM) enterOrderCloseHandle(orderID int64, e *fsm.Event) {
	_, err := g.DB().Model(dao.Order.Table()).Data(g.Map{
		"end_time":     time.Now().Format(time.DateTime),
		"archive_time": time.Now().Format(time.DateTime),
	}).WherePri(orderID).Update()
	if err != nil {
		glog.Errorf(context.Background(), "更新核销时间失败")
		return
	}
}

// 支付后处理
func (this *OrderFSM) afterOrderPayHandle(orderID int64, e *fsm.Event) {

	order, err := service.SysUserOrder().View(this.ctx, &sysin.UserOrderViewInp{Id: orderID})
	if err != nil {
		glog.Errorf(context.Background(), "获取订单失败: %s", err.Error())
		return
	}

	// 查询会员信息
	member, err := service.AdminMember().GetById(this.ctx, &adminin.GetByIdInp{
		Id: order.UserId,
	})
	if err != nil {
		glog.Errorf(context.Background(), "获取用户信息失败: %s", err.Error())
		return
	}

	// 设置用户标签
	defer func() {
		if err == nil {
			simple.SafeGo(this.ctx, func(ctx context.Context) {
				if garray.NewStrArrayFrom(gconv.Strings(member.Label.Array())).ContainsI(consts.AdminMemberLabelGuest) {
					err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
						// 更新用户标签
						if err = service.AdminMember().UpdateInfo(ctx, &adminin.MemberUpdateInfoInp{
							ID:    member.Id,
							Label: gjson.New([]string{consts.AdminMemberLabelNewbie}),
						}); err != nil {
							glog.Errorf(ctx, "修改用户标签失败: %s", err.Error())
						}
						return
					})
				}
			})
		}
	}()

	// 电商发货
	if order.OrderType == consts.OrderTypePointECommerce {
		// 如果是积分电商创建物流信息
		{
			expressInfoInp := expressinfoin.ExpressInfoEditInp{
				ExpressInfo: entity.ExpressInfo{
					OrderUid: order.Uid,
					//StoreId:            order.StoreId,
					ProductId:          order.ProductId,
					UserId:             order.UserId,
					Recipient:          order.Recipient,
					ShippingProvinceId: order.ShippingProvinceId,
					ShippingAddress:    order.ShippingAddress,
					Tel:                order.Tel,
					Status:             consts.ExpressStatusNot,
					ExpressCompany:     consts.ExpressNotSet,
				},
			}
			var exchangeInfoID int64
			exchangeInfoID, err = service.ExpressinfoExpressInfo().Edit(this.ctx, &expressInfoInp)
			if err != nil {
				glog.Errorf(context.Background(), "创建物流信息失败: %s", err.Error())
				return
			}
			if _, err = service.SysUserOrder().Model(this.ctx).WherePri(orderID).Data(g.Map{
				dao.Order.Columns().ExpressInfoId: exchangeInfoID,
			}).Update(); err != nil {
				glog.Errorf(context.Background(), "更新订单物流信息失败: %s", err.Error())
				return
			}
		}

		err = this.PushState(orderID, consts.PayToWaitExpressEvent, e.Args)
		if err != nil {
			glog.Errorf(context.Background(), "变更状态失败: %s", err.Error())
			return
		}
		return
	}

	// 团购订单，并且是会员
	if order.OrderType == consts.OrderTypeGroupOrder && member.MemberStatus == consts.AdminMemberMemberStatusMember {

		//// 增加积分
		//if _, err = service.AdminCreditsLog().SaveIntegral(this.ctx, &adminin.CreditsLogSaveIntegralInp{
		//	MemberId:    order.UserId,
		//	OrderID:     order.Id,
		//	AppId:       contexts.GetModule(this.ctx),
		//	CreditGroup: consts.CreditGroupDecr,
		//	Num:         math.Floor(order.UseAmount / 10), // TODO 人名币积分兑换比例 临时设置
		//	Remark:      fmt.Sprintf("购买商品: %s", order.ProductName),
		//}); err != nil {
		//	glog.Errorf(context.Background(), "增加积分失败,请稍后重试: %s", err.Error())
		//}
	}

	// 会员订单
	if order.OrderType == consts.OrderTypeMemberOrder {

		// 升级 销氪 服务商，并且非线下收货
		if (order.MemberCardId == 4 || order.MemberCardId == 5) && order.ReceiverAddressId > 0 {
			// 变更状态为：待发货
			err = this.PushState(orderID, consts.PayToWaitExpressEvent, e.Args)
			if err != nil {
				glog.Errorf(context.Background(), "变更状态失败: %s", err.Error())
				return
			}
			return
		}

		// 分销
		_ = service.Distribution().PromoPro(gctx.New(), order.Uid)

		// 开通会员
		err = service.AdminMember().OpenMember(this.ctx, &adminin.OpenMemberInp{
			MemberId: member.Id,
			OrderID:  orderID,
			CardId:   order.MemberCardId,
		})

		// 变更状态为：已完成
		err = this.PushState(orderID, consts.PayToDoneEvent, e.Args)
		if err != nil {
			glog.Errorf(context.Background(), "变更状态失败: %s", err.Error())
			return
		}
		return
	}

	err = this.PushState(orderID, consts.PayToWaitVerifyEvent, e.Args)
	if err != nil {
		glog.Errorf(context.Background(), "变更状态失败: %s", err.Error())
		return
	}
	return
}

// 已核销后处理
func (this *OrderFSM) afterOrderVerifyHandle(orderID int64, e *fsm.Event) {
	this.PushState(orderID, consts.VerifyToDoneEvent)
}

// 待核销到已完成后处理
func (this *OrderFSM) afterOrderDoneHandle(orderID int64, e *fsm.Event) {
}

// 已发货后处理
func (this *OrderFSM) afterOrderExpressedHandle(orderID int64, e *fsm.Event) {
}

// 已退款后处理
func (this *OrderFSM) afterOrderReturnedHandle(orderID int64, e *fsm.Event) {
	//this.PushState(orderID, consts.ReturnedToCloseEvent)
}

// 退款中后处理
func (this *OrderFSM) afterOrderReturningHandle(orderID int64, e *fsm.Event) {
	ctx := e.Args[0].(context.Context)
	order, err := service.SysUserOrder().View(ctx, &sysin.UserOrderViewInp{Id: orderID})
	if err != nil {
		glog.Errorf(context.Background(), "获取订单失败: %s", err.Error())
		return
	}
	var refund entity.PayRefund
	if err = service.PayRefund().Model(ctx).Where(dao.PayRefund.Columns().OrderSn, order.Uid).Scan(&refund); err != nil {
		glog.Errorf(context.Background(), "获取退款信息失败: %s", err.Error())
		return
	}
	if refund.Status == consts.RefundStatusAgree {
		this.PushState(orderID, consts.ReturningToReturnedEvent)
		return
	}
}
